001 /*
002 * Copyright 2004 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.util;
020
021 import java.util.Map;
022 import java.beans.Introspector;
023 import java.beans.Expression;
024 import java.lang.reflect.InvocationHandler;
025 import java.lang.reflect.Method;
026 import java.lang.reflect.Proxy;
027
028 /**
029 * Invoication handler utility for a Context inner-class.
030 *
031 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
032 * @version 1.0.1
033 */
034 public final class ContextInvocationHandler implements InvocationHandler
035 {
036 //-------------------------------------------------------------------
037 // static
038 //-------------------------------------------------------------------
039
040 /**
041 * Construct a new context instance implementing the supplied class
042 * and backed by entries in the supplied map.
043 *
044 * @param clazz the context inner class
045 * @param map a map of context entry keys to values
046 * @return the proxied context instance
047 */
048 public static Object getProxiedInstance( Class clazz, Map map )
049 {
050 ClassLoader classloader = clazz.getClassLoader();
051 ContextInvocationHandler handler = new ContextInvocationHandler( map );
052 return Proxy.newProxyInstance( classloader, new Class[]{clazz}, handler );
053 }
054
055 //-------------------------------------------------------------------
056 // state
057 //-------------------------------------------------------------------
058
059 /**
060 * A map containing key values.
061 */
062 private final Map m_map;
063
064 //-------------------------------------------------------------------
065 // constructor
066 //-------------------------------------------------------------------
067
068 /**
069 * Create a context invocation handler.
070 *
071 * @param provider the provider
072 */
073 private ContextInvocationHandler( Map map )
074 {
075 m_map = map;
076 }
077
078 //-------------------------------------------------------------------
079 // implementation
080 //-------------------------------------------------------------------
081
082 /**
083 * Invoke the specified method on underlying object.
084 * This is called by the proxy object.
085 *
086 * @param proxy the proxy object
087 * @param method the method invoked on proxy object
088 * @param args the arguments supplied to method
089 * @return the return value of method
090 * @throws Throwable if an error occurs
091 */
092 public Object invoke( final Object proxy, final Method method, final Object[] args ) throws Throwable
093 {
094 Class source = method.getDeclaringClass();
095 if( Object.class == source )
096 {
097 return method.invoke( this, args );
098 }
099 else
100 {
101 String name = method.getName();
102 if( name.startsWith( "get" ) )
103 {
104 String key = Introspector.decapitalize( name.substring( 3 ) );
105 Object value = m_map.get( key );
106 if( null != value )
107 {
108 Class clazz = method.getReturnType();
109 if( isAssignableFrom( clazz, value.getClass() ) )
110 {
111 return value;
112 }
113 else
114 {
115 Expression expression = new Expression( clazz, "new", new Object[]{value} );
116 return expression.getValue();
117 }
118 }
119 else if( ( null != args ) && args.length > 0 )
120 {
121 return args[0];
122 }
123 else
124 {
125 final String error =
126 "Unable to resolve a context entry value for the key [" + key + "].";
127 throw new IllegalStateException( error );
128 }
129 }
130 throw new UnsupportedOperationException( name );
131 }
132 }
133
134 private static boolean isAssignableFrom( Class clazz, Class c )
135 {
136 if( clazz.isPrimitive() )
137 {
138 if( Integer.TYPE == clazz )
139 {
140 return Integer.class.isAssignableFrom( c );
141 }
142 else if( Boolean.TYPE == clazz )
143 {
144 return Boolean.class.isAssignableFrom( c );
145 }
146 else if( Byte.TYPE == clazz )
147 {
148 return Byte.class.isAssignableFrom( c );
149 }
150 else if( Short.TYPE == clazz )
151 {
152 return Short.class.isAssignableFrom( c );
153 }
154 else if( Long.TYPE == clazz )
155 {
156 return Long.class.isAssignableFrom( c );
157 }
158 else if( Float.TYPE == clazz )
159 {
160 return Float.class.isAssignableFrom( c );
161 }
162 else if( Double.TYPE == clazz )
163 {
164 return Double.class.isAssignableFrom( c );
165 }
166 else
167 {
168 final String error =
169 "Primitive type ["
170 + c.getName()
171 + "] not supported.";
172 throw new RuntimeException( error );
173 }
174 }
175 else
176 {
177 return clazz.isAssignableFrom( c );
178 }
179 }
180 }